⬅ Back

CALLBACK FUNCTIONS IN JAVASCRIPT

This note explains callback functions in simple language.

You will learn:

  1. how a function can be used like a value
  2. what a callback function is
  3. what a higher-order function is
  4. what an inline callback is
  5. how forEach() works

Callback functions are very important in JavaScript. You will see them in:

1. Function as a value

In JavaScript, a function is not only something you can call.

A function can also be treated like a normal value.

That means a function can be:

This makes functions very powerful.

Diagram 1. A function can behave like a value

Function
│
├─ can be called
├─ can be stored
├─ can be passed
└─ can be returned

Explanation

This is one of the most important ideas in JavaScript.

A function is special, but it can still be used like other values.

2. Calling a function vs using the function itself

Look at this example:

function greet(name) {
  return `Welcome ${name}!`;
}

console.log(greet("Aaron")); // "Welcome Aaron!"
console.log(greet); // the function itself

Diagram 2. Parentheses change the meaning

greet("Aaron")
↓
call the function
↓
get the result

greet
↓
do not call the function
↓
use the function itself

Explanation

This is the key difference:

3. Why this matters

If we can use a function as a value, then we can pass it into another function.

That is exactly how callback functions work.

Diagram 3. Function passed into another function

one function
↓
passed as an argument
↓
another function receives it
↓
later calls it

Explanation

This is the base idea of callbacks.

4. Callback functions

A callback function is a function that is passed into another function as an argument.

Then the other function uses it later.

Diagram 4. Callback idea

Callback function
↓
passed into another function
↓
called inside that function

Explanation

So a callback is not special because of its syntax.

It is special because of how it is used.

5. First example: greet and notify

function greet(name) {
  console.log(`Welcome ${name}!`);
}

function notify(name) {
  console.log(`Dear ${name}, your room will be ready in 30 minutes`);
}

These are two normal functions.

They both take a name, but they do different things.

Diagram 5. Two normal functions

greet(name)
→ prints welcome message

notify(name)
→ prints room message

Explanation

Right now, these are just normal functions.

They become callbacks only when we pass them into another function.

6. Higher-order function

Now look at this function:

function registerGuest(name, callback) {
  console.log(`Registering ${name}!`);
  callback(name);
}

This function takes two parameters:

The second parameter is expected to be a function.

Diagram 6. registerGuest structure

registerGuest(name, callback)
│
├─ prints registration message
└─ calls callback(name)

Explanation

This function uses another function passed into it.

That makes it a higher-order function.

7. What is a higher-order function?

A higher-order function is a function that:

or

In this topic, registerGuest is a higher-order function because it takes a function as a parameter.

Diagram 7. Higher-order function meaning

Higher-order function
│
├─ receives a function
or
└─ returns a function

Explanation

In our case, registerGuest receives a function.

8. Using callback functions

Now let's call registerGuest with real callback functions.

function greet(name) {
  console.log(`Welcome ${name}!`);
}

function notify(name) {
  console.log(`Dear ${name}, your room will be ready in 30 minutes`);
}

function registerGuest(name, callback) {
  console.log(`Registering ${name}!`);
  callback(name);
}

registerGuest("Aaron", greet);
// "Registering Aaron!"
// "Welcome Aaron!"

registerGuest("Aaron", notify);
// "Registering Aaron!"
// "Dear Aaron, your room will be ready in 30 minutes"

Diagram 8. First call with greet

registerGuest("Aaron", greet)

Step 1:
name = "Aaron"

Step 2:
callback = greet

Step 3:
print "Registering Aaron!"

Step 4:
callback(name)
↓
greet("Aaron")
↓
print "Welcome Aaron!"

Explanation

greet is passed in as a value.

Inside registerGuest, callback(name) becomes greet(name).

9. Second call with notify

Diagram 9. Second call with notify

registerGuest("Aaron", notify)

Step 1:
name = "Aaron"

Step 2:
callback = notify

Step 3:
print "Registering Aaron!"

Step 4:
callback(name)
↓
notify("Aaron")
↓
print "Dear Aaron, your room will be ready in 30 minutes"

Explanation

This time the same higher-order function works differently, because we passed a different callback.

10. Why callbacks are useful

Callbacks make code more flexible.

The function registerGuest does not need to know exactly what message to show.

It only knows this:

So we can change behavior by passing different callback functions.

Diagram 10. Flexible behavior with callbacks

registerGuest(...)
│
├─ with greet  → welcome message
└─ with notify → room message

Explanation

This is why callbacks are powerful.

One function can work in different ways depending on the callback.

11. Callback parameter name

This parameter name:

callback

is just a name.

It could be called something else, for example:

But callback is a common and clear name.

Diagram 11. Parameter name is flexible

callback
action
fn

All can store a function

Explanation

The important thing is not the name.

The important thing is that the value is a function.

12. Inline callbacks

Sometimes a callback is small and only needed once.

In that case, we can write it directly inside the function call.

This is called an inline callback.

Example

function registerGuest(name, callback) {
  console.log(`Registering ${name}!`);
  callback(name);
}

registerGuest("Aaron", function greet(name) {
  console.log(`Welcome ${name}!`);
});

registerGuest("Benjamin", function notify(name) {
  console.log(`Dear ${name}, your room will be ready in 30 minutes`);
});

Diagram 12. Inline callback idea

registerGuest("Aaron", function (...) {
  ...
})

Callback is written directly inside the call

Explanation

The callback is created exactly where it is needed.

This is useful when the function is small and will not be reused elsewhere.

13. Why inline callbacks are useful

Inline callbacks are useful because:

But if the function is large or reused often, it is usually better to declare it separately.

Diagram 13. Separate callback vs inline callback

Separate callback
→ good for reuse

Inline callback
→ good for short one-time use

Explanation

Both styles are valid.

The better choice depends on the situation.

14. The forEach() method

Now let's connect callbacks to something very important: array methods.

forEach() is an array method that uses a callback function.

Syntax

array.forEach(function callback(element, index, array) {
  // callback body
});

Diagram 14. forEach() structure

array.forEach(callback)

for each element:
call callback(...)

Explanation

forEach() goes through the array and calls the callback once for every element.

15. What forEach() does

forEach():

It is often used instead of for or for...of when you simply want to do something for every element.

Diagram 15. forEach() flow

Array
↓
take first element
↓
call callback

take second element
↓
call callback

take third element
↓
call callback

Explanation

The callback runs again and again for each item in the array.

16. Parameters of the forEach() callback

The callback function in forEach() can receive these parameters:

  1. element - current element
  2. index - current index
  3. array - original array

Example form

array.forEach(function (element, index, array) {
  // code
});

Diagram 16. Callback parameters in forEach()

element → current value
index   → position of current value
array   → original array

Explanation

You do not always need all three.

Most often, you only need element.

17. Example: for loop vs forEach()

const numbers = [5, 10, 15, 20, 25];

// Classic for
for (let i = 0; i < numbers.length; i += 1) {
  console.log(`Index ${i}, value ${numbers[i]}`);
}

// forEach
numbers.forEach(function (number, index) {
  console.log(`Index ${index}, value ${number}`);
});

Diagram 17. for and forEach() do similar work

for loop
→ manually control index

forEach
→ array automatically gives value and index to callback

Explanation

forEach() is shorter and cleaner in many simple cases.

18. Step-by-step forEach() example

const numbers = [5, 10, 15];

numbers.forEach(function (number, index) {
  console.log(`Index ${index}, value ${number}`);
});

Diagram 18. What happens in forEach()

Iteration 1:
number = 5
index = 0

Iteration 2:
number = 10
index = 1

Iteration 3:
number = 15
index = 2

Explanation

For each element, forEach() calls the callback again with fresh values.

19. You do not need all callback parameters

If you only need the current element, you can write only one parameter.

Example

const numbers = [5, 10, 15];

numbers.forEach(function (number) {
  console.log(number);
});

Diagram 19. Using only needed parameters

Need only value?
→ use element only

Need value and index?
→ use element, index

Need all?
→ use element, index, array

Explanation

This keeps the code simpler.

20. forEach() returns undefined

This is important:

forEach() does not return a useful new array or value.

It returns:

undefined

So it is mainly used for actions like:

Diagram 20. Return value of forEach()

forEach(...)
↓
always returns undefined

Explanation

Even if you write return inside the callback, that does not change the return value of forEach() itself.

21. You cannot stop forEach() early

This is one of the biggest differences between forEach() and loops like for or for...of.

With forEach():

It always goes through the whole array.

Diagram 21. forEach() always continues

forEach
↓
goes through all elements
↓
cannot normally stop early

Explanation

If you need early stopping, for or for...of is a better choice.

22. When to use forEach()

Use forEach() when:

Use for or for...of when:

Diagram 22. Choosing between forEach() and loops

Need simple full iteration?
→ forEach()

Need to stop early?
→ for / for...of

Explanation

This is the easiest rule for beginners.

23. Common beginner mistakes

Mistake 1. Calling the callback instead of passing it

Wrong idea:

registerGuest("Aaron", greet());

This calls greet immediately.

Correct:

registerGuest("Aaron", greet);

Here we pass the function itself.

Mistake 2. Forgetting that a callback is just a function value

A callback is not magic.

It is just a function passed as an argument.

Mistake 3. Thinking forEach() returns a new array

It does not.

It returns undefined.

Mistake 4. Thinking forEach() can stop early like break

It cannot do that like a normal loop.

Diagram 23. Common mistakes summary

1. Pass function, do not call it by mistake
2. Callback = function used as an argument
3. forEach() does not return a new array
4. forEach() does not stop early

Explanation

These are very common beginner problems with callbacks.

24. Practical examples

Example 1. Callback with two different behaviors

function sayHello(name) {
  console.log(`Hello ${name}`);
}

function sayBye(name) {
  console.log(`Goodbye ${name}`);
}

function useName(name, callback) {
  callback(name);
}

useName("Nikita", sayHello);
useName("Nikita", sayBye);

Example 2. forEach() with element only

const fruits = ["apple", "banana", "orange"];

fruits.forEach(function (fruit) {
  console.log(fruit);
});

Example 3. forEach() with value and index

const fruits = ["apple", "banana", "orange"];

fruits.forEach(function (fruit, index) {
  console.log(index, fruit);
});

Diagram 24. Real uses of callbacks

Callbacks are used in:
- custom functions
- array methods
- events
- async code

Explanation

Learning callbacks now helps a lot later.

25. Quick summary

Function as a value

A function can be stored or passed just like other values.

Callback function

A function passed as an argument to another function.

Higher-order function

A function that takes another function or returns one.

Inline callback

A callback written directly inside the function call.

forEach()

An array method that calls a callback for each element.

Diagram 25. Final map of callback functions

Callback functions
│
├─ function as value
├─ callback function
├─ higher-order function
├─ inline callback
└─ forEach(callback)

Explanation

These are the main beginner ideas in this topic.

26. Revision block

1. A function can be used as a value
2. A callback is a function passed into another function
3. A higher-order function takes a function or returns one
4. Inline callbacks are written directly in the function call
5. forEach() uses a callback for each array element
6. forEach() can receive element, index, and array
7. forEach() returns undefined
8. forEach() does not stop early like a normal loop

27. Final conclusion

Callback functions are one of the most important foundations in JavaScript.

If you understand:

then you already have a strong base for more advanced JavaScript.

Callbacks are used everywhere in full-stack development:

If you want, I can make the next note about arrow functions in the same style with numbered diagrams.

28. Using Callback Functions and forEach() in JavaScript

A callback function is a function that we pass as an argument to another function.

The second function can call the callback when it needs it.

Example

function sayBye(name) {
  console.log(`Bye ${name}!`);
}

function doSomething(name, callback) {
  callback(name);
}

doSomething("Levi", sayBye);

Output:

Bye Levi!

In this example, sayBye is the callback function.

This line:

doSomething("Levi", sayBye);

means that inside doSomething, JavaScript can call:

callback(name);

and it works like this:

sayBye("Levi");

Part 1: Callback Functions

Exercise 1

Task: Create a function sayBye(name). It should print:

Bye Levi!

Then create this function:

function doSomething(name, callback) {
  callback(name);
}

Then call it like this:

doSomething("Levi", sayBye);

Solution:

function sayBye(name) {
  console.log(`Bye ${name}!`);
}

function doSomething(name, callback) {
  callback(name);
}

doSomething("Levi", sayBye);

Output:

Bye Levi!

Exercise 2

Task: Create a function sayHello(name). It should print:

Hello Levi!

Then create a function doSomething(name, callback). Inside doSomething, call the callback with name.

Then call it like this:

doSomething("Levi", sayHello);

Solution:

function sayHello(name) {
  console.log(`Hello ${name}!`);
}

function doSomething(name, callback) {
  callback(name);
}

doSomething("Levi", sayHello);

Output:

Hello Levi!

Exercise 3

Task: Now add one line before the callback runs.

Create a function sayHello(name). It should print:

Hello Levi!

Create a function doSomething(name, callback). First it should print:

Starting...

Then it should call the callback. At the end, call:

doSomething("Levi", sayHello);

Solution:

function sayHello(name) {
  console.log(`Hello ${name}!`);
}

function doSomething(name, callback) {
  console.log("Starting...");
  callback(name);
}

doSomething("Levi", sayHello);

Output:

Starting...
Hello Levi!

Exercise 4

Task: Now add one line after the callback.

Create a function sayHello(name). Create a function doSomething(name, callback).

Inside doSomething:

  1. Print Starting...
  2. Call the callback
  3. Print Finished.

Expected output:

Starting...
Hello Levi!
Finished.

Solution:

function sayHello(name) {
  console.log(`Hello ${name}!`);
}

function doSomething(name, callback) {
  console.log("Starting...");
  callback(name);
  console.log("Finished.");
}

doSomething("Levi", sayHello);

Output:

Starting...
Hello Levi!
Finished.

Exercise 5

Task: Create a function sayWelcome(name). It should print:

Welcome Levi!

Create a function doSomething(name, callback). Inside doSomething:

  1. Print Starting...
  2. Call the callback
  3. Print Finished.

Then call:

doSomething("Levi", sayWelcome);

Solution:

function sayWelcome(name) {
  console.log(`Welcome ${name}!`);
}

function doSomething(name, callback) {
  console.log("Starting...");
  callback(name);
  console.log("Finished.");
}

doSomething("Levi", sayWelcome);

Output:

Starting...
Welcome Levi!
Finished.

Exercise 6

Task: Create a function sayGoodMorning(name). It should print:

Good morning Levi!

Create a function doSomething(name, callback). Inside doSomething:

  1. Print Starting...
  2. Call the callback
  3. Print Finished.

Then call:

doSomething("Levi", sayGoodMorning);

Solution:

function sayGoodMorning(name) {
  console.log(`Good morning ${name}!`);
}

function doSomething(name, callback) {
  console.log("Starting...");
  callback(name);
  console.log("Finished.");
}

doSomething("Levi", sayGoodMorning);

Output:

Starting...
Good morning Levi!
Finished.

Exercise 7

Task: This time the callback should use two arguments: name and room.

Create a function showRoom(name, room). It should print:

Levi is in room 204.

Create a function registerGuest(name, room, callback). Inside registerGuest:

  1. Print Registering Levi...
  2. Call the callback with name and room

Then call:

registerGuest("Levi", 204, showRoom);

Solution:

function showRoom(name, room) {
  console.log(`${name} is in room ${room}.`);
}

function registerGuest(name, room, callback) {
  console.log(`Registering ${name}...`);
  callback(name, room);
}

registerGuest("Levi", 204, showRoom);

Output:

Registering Levi...
Levi is in room 204.

Exercise 8

Task: Now create two different callback functions.

Create a function showRoom(name, room). It should print:

Levi is in room 204.

Create a function sendMessage(name, room). It should print:

Dear Levi, your room number is 204.

Create a function registerGuest(name, room, callback). Inside registerGuest:

  1. Print Registering Levi...
  2. Call the callback with name and room

Then call registerGuest two times:

registerGuest("Levi", 204, showRoom);
registerGuest("Levi", 204, sendMessage);

Solution:

function showRoom(name, room) {
  console.log(`${name} is in room ${room}.`);
}

function sendMessage(name, room) {
  console.log(`Dear ${name}, your room number is ${room}.`);
}

function registerGuest(name, room, callback) {
  console.log(`Registering ${name}...`);
  callback(name, room);
}

registerGuest("Levi", 204, showRoom);
registerGuest("Levi", 204, sendMessage);

Output:

Registering Levi...
Levi is in room 204.
Registering Levi...
Dear Levi, your room number is 204.

Exercise 9

Task: Create two callback functions: showPrice(product, price) and showDiscount(product, price).

showPrice should print:

Apple costs 2 euros.

showDiscount should print:

Apple has a discount. New price: 2 euros.

Create a function checkProduct(product, price, callback). Inside checkProduct:

  1. Print Checking Apple...
  2. Call the callback with product and price

Then call:

checkProduct("Apple", 2, showPrice);
checkProduct("Apple", 2, showDiscount);

Solution:

function showPrice(product, price) {
  console.log(`${product} costs ${price} euros.`);
}

function showDiscount(product, price) {
  console.log(`${product} has a discount. New price: ${price} euros.`);
}

function checkProduct(product, price, callback) {
  console.log(`Checking ${product}...`);
  callback(product, price);
}

checkProduct("Apple", 2, showPrice);
checkProduct("Apple", 2, showDiscount);

Output:

Checking Apple...
Apple costs 2 euros.
Checking Apple...
Apple has a discount. New price: 2 euros.

Exercise 10

Task: Create two callback functions: makeUpperCase(word) and makeLowerCase(word).

makeUpperCase should print the word in big letters.

makeLowerCase should print the word in small letters.

Create a function changeWord(word, callback). Inside changeWord:

  1. Print Changing Hello...
  2. Call the callback with word

Use these methods:

word.toUpperCase()
word.toLowerCase()

Then call:

changeWord("Hello", makeUpperCase);
changeWord("Hello", makeLowerCase);

Solution:

function makeUpperCase(word) {
  console.log(word.toUpperCase());
}

function makeLowerCase(word) {
  console.log(word.toLowerCase());
}

function changeWord(word, callback) {
  console.log(`Changing ${word}...`);
  callback(word);
}

changeWord("Hello", makeUpperCase);
changeWord("Hello", makeLowerCase);

Output:

Changing Hello...
HELLO
Changing Hello...
hello

Part 2: Inline Callbacks and forEach()

forEach() runs a function one time for every item in an array.

We can use forEach() with a normal function.

Example

const names = ["Levi", "Aaron", "Benjamin"];

names.forEach(function(name) {
  console.log(name);
});

Output:

Levi
Aaron
Benjamin

This part is the callback:

function(name) {
  console.log(name);
}

It means: take each item from the array and use it as name.

Exercise 11

Task: Create this array:

const fruits = ["Apple", "Banana", "Orange"];

Use forEach() with a normal function to print every fruit.

Expected output:

Apple
Banana
Orange

Solution:

const fruits = ["Apple", "Banana", "Orange"];

fruits.forEach(function(fruit) {
  console.log(fruit);
});

Exercise 12

Task: Create this array:

const numbers = [1, 2, 3, 4];

Use forEach() with a normal function to print every number.

Expected output:

1
2
3
4

Solution:

const numbers = [1, 2, 3, 4];

numbers.forEach(function(number) {
  console.log(number);
});

Exercise 13

Task: Create this array:

const numbers = [1, 2, 3, 4];

Use forEach() with a normal function to print every number multiplied by 2.

Expected output:

2
4
6
8

Solution:

const numbers = [1, 2, 3, 4];

numbers.forEach(function(number) {
  console.log(number * 2);
});

Exercise 14

Task: Create this array:

const numbers = [1, 2, 3, 4];

Use forEach() with a normal function to print this text:

Double of 1 is 2
Double of 2 is 4
Double of 3 is 6
Double of 4 is 8

Use number * 2 to calculate the double.

Solution:

const numbers = [1, 2, 3, 4];

numbers.forEach(function(number) {
  console.log(`Double of ${number} is ${number * 2}`);
});

Exercise 15

Task: Create this array:

const numbers = [1, 2, 3, 4];

Use forEach() with a normal function to print every number plus 10.

Expected output:

11
12
13
14

Solution:

const numbers = [1, 2, 3, 4];

numbers.forEach(function(number) {
  console.log(number + 10);
});

Exercise 16

Task: Create this array:

const names = ["Levi", "Aaron", "Benjamin"];

Use forEach() with a normal function to print:

Hello Levi!
Hello Aaron!
Hello Benjamin!

Solution:

const names = ["Levi", "Aaron", "Benjamin"];

names.forEach(function(name) {
  console.log(`Hello ${name}!`);
});

Exercise 17

Task: Create this array:

const products = ["Milk", "Bread", "Cheese"];

Use forEach() with a normal function to print:

Product: Milk
Product: Bread
Product: Cheese

Solution:

const products = ["Milk", "Bread", "Cheese"];

products.forEach(function(product) {
  console.log(`Product: ${product}`);
});

Exercise 18

Task: Create this array:

const prices = [10, 20, 30];

Use forEach() with a normal function to print:

Price: 10 euros
Price: 20 euros
Price: 30 euros

Solution:

const prices = [10, 20, 30];

prices.forEach(function(price) {
  console.log(`Price: ${price} euros`);
});

Exercise 19

Task: Create this array:

const prices = [10, 20, 30];

Use forEach() with a normal function to print every price with tax.

Tax is calculated like this:

price * 1.2

Expected output:

Price with tax: 12 euros
Price with tax: 24 euros
Price with tax: 36 euros

Solution:

const prices = [10, 20, 30];

prices.forEach(function(price) {
  console.log(`Price with tax: ${price * 1.2} euros`);
});
⬅ Back